﻿
#if !NET45
using Dapper;
#endif
using RM.BasicLibrary.Models.Databases;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace RM.BasicLibrary.Models.Repositorys
{
    public abstract class RepositoryAbstract : IRepository
    {
        Action<DbModelBuilder> OnModelCreate { get; set; }
        public void SetModels(Action<DbModelBuilder> onModelCreate)
        {
            OnModelCreate = onModelCreate;
        }
        public IRepository Create()
        {
            Database.NotNull();
            Database?.ConnectionString.NotNull();
            OnModelCreate.NotNull(nameof(SetModels));
            Database.DBcontext?.Dispose();
            Dispose();
            Database.CreateDbContext();
            (Database.DBcontext as IDBContext).OnModelCreate = OnModelCreate;
            Database.DBcontext.Database.CreateIfNotExists();
            return this;
        }

        public IRepository Create(IDatabase database = null)
        {
            if (!database.IsNull())
                Database = database;
            return this;
        }
        protected DbTransaction dbTransaction { get; set; }
        public RepositoryAbstract()
        {

        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="idatabase"></param>
        public RepositoryAbstract(IDatabase idatabase)
        {
            this.Database = idatabase;
        }
        public IDatabase Database { get; set; }

        public IRepository BeginTrans()
        {
            if (Database.DBcontext.Database.Connection.State == ConnectionState.Closed)
            {
                Database.DBcontext.Database.Connection.Open();
            }
            dbTransaction = Database.DBcontext.Database.Connection.BeginTransaction();
            Database.DBcontext.Database.UseTransaction(dbTransaction);
            return this;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public int Commit()
        {
            int returnValue = 0;
            Root.BasicIns.ExFilter.Execute(() =>
            {
                returnValue = Database.DBcontext.SaveChanges();
                if (dbTransaction != null) dbTransaction.Commit();
            });
            return returnValue;
        }
        /// <summary>
        /// 把当前操作回滚成未提交状态
        /// </summary>
        public void Rollback()
        {
            this.dbTransaction?.Rollback();
            this.Dispose();
        }

        public void Dispose()
        {
            this.dbTransaction?.Dispose();
            Database.DBcontext?.Dispose();
        }

        public int Insert<T>(T entity) where T : class
        {
            Create();
            Database.DBcontext.Entry<T>(entity).State = EntityState.Added;
            return Commit();
        }

        public int Insert<T>(List<T> entities) where T : class
        {
            Create();
            foreach (var entity in entities)
                Database.DBcontext.Entry<T>(entity).State = EntityState.Added;
            return Commit();
        }
        public int Delete<T>(T entity) where T : class
        {
            Create();
            Database.DBcontext.Set<T>().Attach(entity);
            Database.DBcontext.Set<T>().Remove(entity);
            return Commit();
        }

        public int Delete<T>(List<T> entities) where T : class
        {
            Create();
            foreach (var entity in entities)
            {
                Database.DBcontext.Set<T>().Attach(entity);
                Database.DBcontext.Set<T>().Remove(entity);
            }
            return Commit();
        }

        public int Delete<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            Create();
            IEnumerable<T> entities = Database.DBcontext.Set<T>().Where(condition).ToList();
            return entities.Count() > 0 ? Delete(entities) : 0;
        }
        public int Update<T>(T entity, Action<DbPropertyEntry> actionFilter) where T : class
        {
            Create();
            this.UpdateEntity(entity, actionFilter);
            return Commit();
        }
        /// <summary>
        /// 更新实体数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="entity">实体数据</param>
        /// <returns></returns>
        public int Update<T>(T entity) where T : class
        {
            Create();
            Database.DBcontext.Set<T>().Attach(entity);
            Database.DBcontext.Entry(entity).State = EntityState.Modified;
            return Commit();
        }
        public int Update<T>(List<T> entities, Action<DbPropertyEntry> actionFilter = null) where T : class
        {
            Create();
            foreach (var entity in entities)
                this.UpdateEntity(entity, actionFilter);
            return Commit();
        }
        /// <summary>
        /// EF更新实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="entity">实体数据</param>
        private void UpdateEntity<T>(T entity, Action<DbPropertyEntry> actionFilter = null) where T : class
        {
            Create();
            Database.DBcontext.Set<T>().Attach(entity);
            Hashtable props = Extension.GetPropertyInfo<T>(entity);
            foreach (string item in props.Keys)
            {
                object value = Database.DBcontext.Entry(entity).Property(item).CurrentValue;
                if (value.ToString() == "&nbsp;")
                    Database.DBcontext.Entry(entity).Property(item).CurrentValue = null;
                if (!value.IsNull())
                    Database.DBcontext.Entry(entity).Property(item).IsModified = true;
                actionFilter?.Invoke(Database.DBcontext.Entry(entity).Property(item));
            }
        }
        public IQueryable<T> IQueryable<T>() where T : class, new()
        {
            Create();
            return Database.DBcontext.Set<T>();
        }

        public IQueryable<T> IQueryable<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            Create();
            return Database.DBcontext.Set<T>().Where(condition);
        }

        public T Query<T>(object keyValue) where T : class
        {
            Create();
            return Database.DBcontext.Set<T>().Find(keyValue);
        }

        public T Query<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            Create();
            return Database.DBcontext.Set<T>().Where(condition).FirstOrDefault();
        }
#if !NET45
        public T Query<T>(string strSql, object dbParameter) where T : class, new()
        {
            Create();
            var data = Database.DBcontext.Database.Connection.Query<T>(strSql, dbParameter);
            return data.FirstOrDefault();
        }
        public IEnumerable<T> Querys<T>(string strSql) where T : class
        {
            Create();
            return Database.DBcontext.Database.Connection.Query<T>(strSql);
        }

        public IEnumerable<T> Querys<T>(string strSql, object dbParameter) where T : class
        {
            Create();
            return Database.DBcontext.Database.Connection.Query<T>(strSql, dbParameter, dbTransaction);
        }
        public IEnumerable<T> Querys<T>(string strSql, Pagination pagination) where T : class
        {
            Create();
            return Querys<T>(strSql: strSql, dbParameter: null, pagination: pagination);
        }

        public IEnumerable<T> Querys<T>(string strSql, object dbParameter, Pagination pagination) where T : class
        {
            Create();
            StringBuilder sb = new StringBuilder();
            sb.Append(Extension.SqlPageSql(strSql, pagination.Sidx, pagination.Sord.ToLower() == "asc", pagination.Rows, pagination.Page));
            pagination.Records = Convert.ToInt32(Database.DBcontext.Database.Connection.ExecuteScalar("Select Count(1) From (" + strSql + ") As t", dbParameter));
            return Database.DBcontext.Database.Connection.Query<T>(sb.ToString(), dbParameter);
        }
#endif
        public IEnumerable<T> Querys<T>() where T : class, new()
        {
            Create();
            return Database.DBcontext.Set<T>().ToList();
        }


        public IEnumerable<T> Querys<T>(Pagination pagination) where T : class, new()
        {
            Create();
            var queryable = Database.DBcontext.Set<T>().AsQueryable();
            return BaseQuerys(queryable, pagination);
        }
        private IEnumerable<T> BaseQuerys<T>(IQueryable<T> queryable, Pagination pagination) where T : class, new()
        {
            Create();
            int total = pagination.Records;
            MethodCallExpression resultExp = null;
            bool isPage = !pagination.Sidx.IsNull();
            if (isPage)
            {
                pagination.Sidx.Split(',').ToList().ForEach(
                    (x) =>
                    {
                        bool isAsc = pagination.Sord.ToUpper().Contains("ASC") || x.ToUpper().Contains("ASC");
                        var parameter = Expression.Parameter(typeof(T), "t");
                        var property = typeof(T).GetProperty(x);
                        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                        var orderByExp = Expression.Lambda(propertyAccess, parameter);
                        resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(T), property.PropertyType }, queryable.Expression, Expression.Quote(orderByExp));
                    });
            }
            if (resultExp.IsNull()) queryable = queryable.Provider.CreateQuery<T>(resultExp);
            total = queryable.Count();
            queryable = queryable.Skip<T>(pagination.Rows * (pagination.Page - 1)).Take<T>(pagination.Rows).AsQueryable();
            pagination.Records = total;
            return queryable.ToList();
        }

        public IEnumerable<T> Querys<T>(Expression<Func<T, bool>> condition, Pagination pagination) where T : class, new()
        {
            Create();
            var queryable = Database.DBcontext.Set<T>().Where(condition);
            return BaseQuerys(queryable, pagination);
        }

        public IEnumerable<T> Querys<T>(Expression<Func<T, bool>> condition) where T : class, new()
        {
            Create();
            return Database.DBcontext.Set<T>().Where(condition).ToList();
        }


    }
}
